<?php

/**
 * This is the model class for table "{{category}}".
 *
 * The followings are the available columns in table '{{category}}':
 * @property integer $id
 * @property integer $parent_id
 * @property string $name
 * @property string $alias
 * @property integer $level
 * @property integer $display_order
 * @property string $description
 * @property integer $status
 * @property string $extension
 * @property string $language
 * @property string $meta_description
 * @property string $meta_keyword
 * @property string $robots
 * @property string $created_by
 * @property string $create_date
 * @property string $updated_by
 * @property string $update_date
 * @property integer $hits
 */
class Category extends CActiveRecord {

    // parent_id of category before ordering
    public $oldParentId;
    public $controllerExtension;
    public $icon = NULL;
    public $childs = array();
    public $level = 0;
    public $isUp = NULL;
    public $isDown = NULL;

    /*
     * Save status is loaded from database. See more AfterFind
     */
    public $originalStatus;

    const SCENARIO_SEARCH = 'search';
    const SCENARIO_CREATE = 'create';
    const SCENARIO_UPDATE = 'update';
    const SCENARIO_ORDER = 'order';
    const ACTION_DOWN = 'down';
    const ACTION_UP = 'up';
    const ALIAS_MAX_LENGTH = 50;

    public function init() {
	parent::init();
	$this->controllerExtension = LGConstant::DEFAULT_CATEGORY_EXTENSION;
    }

    /**
     * Returns the static model of the specified AR class.
     * @param string $className active record class name.
     * @return LgcmsCategory the static model class
     */
    public static function model($className = __CLASS__) {
	return parent::model($className);
    }

    /**
     * @return string the associated database table name
     */
    public function tableName() {
	return '{{category}}';
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules() {
	// NOTE: you should only define rules for those attributes that
	// will receive user inputs.
	return array(
	    array('name, status, language, parent_id', 'required', 'message' => t('MSG_REQUIRED', 'messages')),
	    array('name, meta_keyword', 'length', 'max' => 255),
	    array('alias', 'length', 'max' => Category::ALIAS_MAX_LENGTH),
	    array('status', 'checkTriggerParentStatus', 'on' => Category::SCENARIO_CREATE . ',' . Category::SCENARIO_UPDATE, 'message' => t('MSG_PARENT_STATUS_IS_UNPUBLISHED', 'messages')),
	    array('description', 'length', 'max' => 1000, 'tooLong' => t('MSG_STRING_TOO_LONG', 'messages'), 'on' => Category::SCENARIO_CREATE . ', ' . Category::SCENARIO_UPDATE),
	    array('extension, meta_description', 'length', 'max' => 500, 'tooLong' => t('MSG_STRING_TOO_LONG', 'messages')),
	    array('id, originalStatus, oldParentId,  catExtension, parent_id, name, alias, level, display_order, description, status, extension, language, meta_description, meta_keyword, robots, created_by, create_date, updated_by, update_date, hits', 'safe'),
	);
    }

    /*
     * Check rule: 
     * 	    If a category is changed to published status, and any its super nodes have an unpublished status. This action will be save and show messsage 'Cannot change the published status when the parent category is of a unpublished status.'
     */

    public function checkTriggerParentStatus($attribute, $params) {

	if ($this->status == LGConstant::PUBLISH_STATUS) {
	    if (Category::AreAllSuperCategoriesUnpusblishedStatus($this)) {
		$message = isset($params['message']) ?
			$params['message'] : t('MSG_PARENT_STATUS_IS_UNPUBLISHED', 'messamge');
		$this->addError($attribute, $message);
	    }
	}
    }

    public static function AreAllSuperCategoriesUnpusblishedStatus($categoryModel) {
	$parentCategory = $categoryModel->getParentCategory();

	if ($parentCategory == null) {
	    return false;
	}

	if ($parentCategory->status == LGConstant::UNPUBLISH_STATUS) {
	    return true;
	}

	return Category::AreAllSuperCategoriesUnpusblishedStatus($parentCategory);
    }

    /**
     * 
     * @return 
     */
    public function getParentCategory() {

	if (!isset($this->parent_id) || empty($this->parent_id)) {
	    return null;
	}
	return $this->findByPk($this->parent_id);
    }

    /**
     * @return array relational rules.
     */
    public function relations() {
	// NOTE: you may need to adjust the relation name and the related
	// class name for the relations automatically generated below.
	return array(
	);
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels() {
	return array(
	    'id' => t('LBL_ID', 'category'),
	    'parent_id' => t('LBL_PARENT', 'category'),
	    'name' => t('LBL_NAME', 'category'),
	    'alias' => t('LBL_ALIAS', 'category'),
	    'level' => t('LBL_LEVEL', 'category'),
	    'display_order' => t('LBL_DISPLAY_ORDER', 'category'),
	    'description' => t('LBL_DESCRIPTION', 'category'),
	    'status' => t('LBL_STATUS', 'category'),
	    'extension' => t('LBL_EXTENSION', 'category'),
	    'language' => t('LBL_LANGUAGE', 'category'),
	    'meta_description' => t('LBL_META_DESCRIPTION', 'category'),
	    'meta_keyword' => t('LBL_META_KEYWORD', 'category'),
	    'robots' => t('LBL_ROBOTS', 'category'),
	    'created_by' => t('LBL_CREATED_BY', 'category'),
	    'create_date' => t('LBL_CREATE_DATE', 'category'),
	    'updated_by' => t('LBL_UPDATED_BY', 'category'),
	    'update_date' => t('LBL_UPDATE_DATE', 'category'),
	    'hits' => t('LBL_HITS', 'category'),
	);
    }

    /**
     * Retrieves a list of models based on the current search/filter conditions.
     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
     */
    public function search() {
	// Warning: Please modify the following code to remove attributes that
	// should not be searched.

	$criteria = new CDbCriteria;

	$criteria->compare('id', $this->id);
	$criteria->compare('parent_id', $this->parent_id);
	$criteria->addSearchCondition('name', trim($this->name));
	$criteria->compare('alias', $this->alias, true);
	$criteria->compare('level', $this->level);
	$criteria->compare('display_order', $this->display_order);
	$criteria->compare('description', $this->description, true);
	$criteria->compare('status', $this->status);
	$criteria->compare('extension', $this->extension, true);
	$criteria->compare('language', $this->language, true);
	$criteria->compare('meta_description', $this->meta_description, true);
	$criteria->compare('meta_keyword', $this->meta_keyword, true);
	$criteria->compare('robots', $this->robots, true);
	$criteria->compare('created_by', $this->created_by, true);
	$criteria->compare('create_date', $this->create_date, true);
	$criteria->compare('updated_by', $this->updated_by, true);
	$criteria->compare('update_date', $this->update_date, true);
	$criteria->compare('hits', $this->hits);

	return new CActiveDataProvider($this, array(
	    'criteria' => $criteria,
	));
    }

    public function getMaxDisplayOrder($parentId, $extension) {
	$criteria = new CDbCriteria();
	$criteria->select = 'max(display_order) as display_order';
//        $criteria->condition = 'extension = :extension';
//        $criteria->addCondition('parent_id = :parent_id');
//        $criteria->params = array(':parent_id' => $parentId, ':extension' => $extension);

	$criteria->compare('extension', $extension);
	$criteria->compare('parent_id', $parentId);

	return $this->find($criteria);
    }

    public function getMinDisplayOrder($parentId, $extension) {
	$criteria = new CDbCriteria();
	$criteria->select = 'min(display_order) as display_order';

	$criteria->compare('extension', $extension);
	$criteria->compare('parent_id', $parentId);

	return $this->find($criteria);
    }

    /*
     * When a category changes status, all its subs will be change to its status.
     * return true if updated successfully. Else return false. Add error to model if occur when validation.
     */

    public function changeStatus($newStatus) {

	if ($this->status != $newStatus) {
	    $this->status = $newStatus;
	    if (!$this->validate(array('status'))) {
		return false;
	    }
	    $transaction = $this->dbConnection->beginTransaction();

	    $isSuccess = true;
	    if ($this->saveAttributes(array('status'))) {
		if (!$this->changeAllSubsStatus()) {
		    $isSuccess = false;
		}
	    } else {
		$isSuccess = false;
	    }

	    if ($isSuccess) {
		$transaction->commit();
	    } else {
		$transaction->rollback();
	    }

	    return $isSuccess;
	} else {
	    return false;
	}
    }

    public function searchByDisplayOrder($order, $parentId, $extension) {
	$criteria = new CDbCriteria();

	$criteria->compare('extension', $extension);
	$criteria->compare('display_order', $order);
	$criteria->compare('parent_id', $parentId);

	$result = $this->findAll($criteria);
	return $result[0];
    }

    /**
     * Get All category of a parentId and extension
     * @param type $parentId
     * @param type $extension
     * @return type
     */
    public function searchByParentID($parentId, $extension = null) {
	$criteria = new CDbCriteria();

	$criteria->compare('extension', $extension);
	$criteria->compare('parent_id', $parentId);

	return $this->findAll($criteria);
    }

    public function getMenus() {
	$criteria = new CDbCriteria();

	$criteria->compare('extension', LGConstant::CATEGORY_EXTENSION_OF_MENU, FALSE);
	//$criteria->order = 'display_order ASC';
	return $this->findAll($criteria);
    }

    function getDataForParentDropDownList($extension, $arr = NULL, $currentId = 0) {
	if (!$arr)
	    $arr = array();
	$tempArray = array();
	$finalArray = array();
	$arr = LGHelpers::buildTreeArray($this->getAllParentAndChildCategory($extension, '', ''));
	LGHelpers::exposeTreeArray($arr, $tempArray, '— ');

	//Only apply for category module
	if ($currentId != 0) {
	    $category = $this->getAllCategoryArrayHaveRootSpecifiedID($currentId, $extension);
	    if (count($category) > 0) {
		foreach ($category as $model) {
		    foreach ($tempArray as $index => $value) {
			if ($model['id'] == $value['id'] || $value['id'] == $currentId) {
			    unset($tempArray[$index]);
			}
		    }
		}
	    } else {
		foreach ($tempArray as $index => $value) {
		    if ($value['id'] == $currentId) {
			unset($tempArray[$index]);
		    }
		}
	    }
	}
	foreach ($tempArray as $model) {
	    if ($model['status'] == LGConstant::UNPUBLISH_STATUS) {
		$finalArray[] = array(
		    'id' => $model['id'],
		    'name' => $model['indent'] . '[' . $model['name'] . ']');
	    } else {
		$finalArray[] = array(
		    'id' => $model['id'],
		    'name' => $model['indent'] . $model['name']);
	    }
	}
	return $finalArray;
    }

//    /**
//     * Create HTML for order_display, includes 2 icons: Up and Down, with rules
//     * 	- If item is top, icon up will be disable
//     * 	- if item is bottom, icon down will be disable
//     *  - if item is top and is bottom (it's mean item is exclusive item in it's parent), it will disable 2 icons
//     * @param type $extension
//     * @param type $arr
//     * @return string
//     */
//    public function getDataForIndex($extension, $arr = NULL) {
//
//	if (!$arr)
//	    $arr = array();
//
//	//Result array
//	$finalArray = array();
//
//	$arr = LGHelpers::buildTreeArray($this->getAllParentAndChildCategory($extension, $this->status, $this->name));
//	LGHelpers::exposeTreeArray($arr, $finalArray, '|— ');
//	$count = 0;
//
//	//some Constants
//	$actionUp = LGConstant::ACTION_UP;
//	$actionDown = LGConstant::ACTION_DOWN;
//
//	$classIconUp = "icon $actionUp";
//	$classIconDown = "icon $actionDown";
//
//	$classDisableIconUp = "icon $actionUp disable";
//	$classDisableIconDown = "icon $actionDown disable";
//
//	$buttonFormat = "<input class='%s' onclick='js:LGCMS.ajaxCallback(\"%s\")' name='yt2' type='button'>";
//	$disableButtonFormat = "<input class='%s' type='button'>";
//
//	foreach ($finalArray as $data) {
//	    $max_order = $this->getMaxDisplayOrder($data['parent_id'], $extension);
//	    $min_order = $this->getMinDisplayOrder($data['parent_id'], $extension);
//
//	    $urlDown = Yii::app()->baseUrl . "/category/order?id=" . $data['id']
//		    . "&order=" . $data['display_order']
//		    . "&action=$actionDown&extension=" . $extension;
//	    $buttonIconDown = sprintf($buttonFormat, $classIconDown, $urlDown);
//
//	    $urlUp = Yii::app()->baseUrl . "/category/order?id=" . $data['id']
//		    . "&order=" . $data['display_order']
//		    . "&action=$actionUp&extension=" . $extension;
//	    $buttonIconUp = sprintf($buttonFormat, $classIconUp, $urlUp);
//
//	    //If this record is top, disable Up button 
//	    if ((int) $data['display_order'] == (int) $min_order['display_order']) {
//		$buttonIconUp = sprintf($disableButtonFormat, $classDisableIconUp);
//	    }
//
//	    //If this record is bottm, disable Down button 
//	    if ((int) $data['display_order'] == (int) $max_order['display_order']) {
//		$buttonIconDown = sprintf($disableButtonFormat, $classDisableIconDown);
//	    }
//
//	    //data of order_column is button down and button up
//	    $finalArray[$count]['order_column'] = $buttonIconDown . $buttonIconUp;
//	    $count++;
//	}
//
//	return $finalArray;
//    }

    /**
     * Get 
     * @param type $extension
     * @param type $statusFilter
     * @param type $nameSearch
     * @return Array
     */
    public function getAllParentAndChildCategory($extension, $statusFilter = '', $nameSearch = '') {
	$arr = array();
	$criteria = new CDbCriteria();

	//TODO : Fixed by Kien.Tran
	$criteria->compare('extension', $extension);
	if ($statusFilter != '') {
	    $criteria->compare('status', $statusFilter);
	}

	if ($nameSearch != '') {
	    $criteria->addSearchCondition('name', $nameSearch, true);
	}

	$categoryData = $this->findAll($criteria, array('order' => 'display_order ASC'));

	foreach ($categoryData as $model) {
	    $arr[$model->id] = array('id' => $model['id'],
		'parent_id' => $model['parent_id'],
		'order' => $model['display_order'],
		'name' => $model['name'],
		'description' => $model['description'],
		'status' => $model['status'],
		'display_order' => $model['display_order'],
		'updated_by' => $model['updated_by'],
		'update_date' => $model['update_date'],
		'order_column' => '',
	    );
	}

	return $arr;
    }

    public function beforeSave() {
	if (parent::beforeSave()) {
	    $extension = $this->controllerExtension;

	    $current_date = date(LGConstant::DATE_FORMAT_FOR_DATABASE);
	    $logedInUser = User::model()->findByPk(Yii::app()->user->id);

	    if ($this->alias == NULL) {
		$this->alias = substr(stringSafe($this->name), 0, Category::ALIAS_MAX_LENGTH);
	    } else {
		$this->alias = stringSafe($this->alias);
	    }

	    // When create a new category , adding it to the end of list
	    if ($this->display_order == 0) {
		$max_category = $this->getMaxDisplayOrder($this->parent_id, $extension);
		$this->display_order = $max_category['display_order'] + 1;
	    }

	    if ($this->isNewRecord) {
		$this->extension = $extension;
		$this->create_date = $current_date;
		$this->created_by = $logedInUser->username;
	    }

	    $this->update_date = $current_date;
	    $this->updated_by = $logedInUser->username;

	    // When updating a child category to a parent category, moving it to the end of list
	    if ($this->oldParentId != 0 && $this->parent_id == 0) {
		$max_category = $this->getMaxDisplayOrder($this->parent_id, $extension);
		$this->display_order = $max_category['display_order'] + 1;
	    } else if ($this->oldParentId != 0 && $this->parent_id != 0 && $this->oldParentId != $this->parent_id) {
		$max_category = $this->getMaxDisplayOrder($this->parent_id, $extension);
		$this->display_order = $max_category['display_order'] + 1;
	    } else if ($this->oldParentId != null && ($this->oldParentId == 0 && $this->parent_id != 0)) {
		$max_category = $this->getMaxDisplayOrder($this->parent_id, $extension);
		$this->display_order = $max_category['display_order'] + 1;
	    }

	    return true;
	}
	else
	    return false;
    }

    function afterSave() {
	$extension = $this->controllerExtension;

	if (!$this->isNewRecord) {

	    //Get categories which have the same parent id to current category
	    $category = $this->searchByParentID($this->parent_id, $extension);
	    $count = 1;

	    //Update display_order number for each category
	    foreach ($category as $data) {
		$child_category = $this->findByPk($data['id']);
		$child_category->display_order = $count;
		$child_category->saveAttributes(array('display_order'));
		$count++;
	    }

	    $parent = $this->findByPk($this->parent_id);

	    if ($parent != NULL) {
		$list_category_parent = $this->searchByParentID($parent->parent_id, $extension);
		$count_parent = 1;
		foreach ($list_category_parent as $value) {
		    $child_category_parent = $this->findByPk($value['id']);
		    $child_category_parent->display_order = $count_parent;
		    $child_category_parent->saveAttributes(array('display_order'));
		    $count_parent++;
		}
	    }

	    if ($this->parent_id != $this->oldParentId) {

		//Get All categories of old parent
		$old_category = $this->searchByParentID($this->oldParentId, $extension);
		$count_old_parent = 1;

		//Update display_order number for each category
		foreach ($old_category as $data) {
		    $child_category = $this->findByPk($data['id']);
		    $child_category->display_order = $count_old_parent;
		    $child_category->saveAttributes(array('display_order'));
		    $count_old_parent++;
		}
	    }

	    if ($this->originalStatus != $this->status) {
		$this->changeAllSubsStatus();
	    }
	}
	parent::afterSave();
    }

//    public function getListCategory() {
//	$extension = $this->controllerExtension;
//
//	$paginate = new CPagination();
//	$paginate->pageSize = LGHelpers::getPageSize($this->count());
//	$myArr = $this->getDataForIndex($extension);
//
//	$dataProvider = new CArrayDataProvider($myArr);
//	$dataProvider->pagination = $paginate;
//
//	$sort = new CSort;
//	$sort->attributes = array(
//	    'display_order',
//	);
//	$sort->defaultOrder = array(
//		//'display_order' => CSort::SORT_ASC,
//	);
//	$dataProvider->sort = $sort;
//	return $dataProvider;
//    }

    public function getListDataForParentDropDownList($id = 0, $extension = LGConstant::DEFAULT_CATEGORY_EXTENSION) {
	$listData = $this->getDataForParentDropDownList($extension, NULL, $id);
	return CHtml::listData($listData, 'id', 'name');
    }

    public function getStatus() {
	return CHtml::listData(LGHelpers::getStatusCategory(), 'id', 'name');
    }

    public function getRobots() {
	return CHtml::listData(LGHelpers::getRobots(), 'id', 'name');
    }

    public function getCategoryForUpdate($parentId, $display_order, $extension) {
	$criteria = new CDbCriteria();

	$criteria->compare('extension', $extension);
	$criteria->compare('parent_id', $parentId);
	$criteria->addCondition('display_order >= :display_order');
	$criteria->params = array_merge($criteria->params, array(':display_order' => $display_order));

	$criteria->order = 'display_order ASC';
	return $this->findAll($criteria);
    }

    /**
     * Get all node have root node which has id is $id
     * @param type $id
     * @param type $extension
     * @param type $arr
     * @return array{'id', 'name'}
     */
    public function getAllCategoryArrayHaveRootSpecifiedID($id, $extension, $arr = NULL) {
	if (!$arr)
	    $arr = array();

	$category = $this->searchByParentID($id, $extension);
	foreach ($category as $data) {
	    $arr[] = array('id' => $data['id'], 'name' => $data['name']);
	    $arr = $this->getAllCategoryArrayHaveRootSpecifiedID($data['id'], $extension, $arr);
	}
	return $arr;
    }

    /**
     * Get all node have root node which has id is $id
     * @param type $id
     * @param type $extension
     * @param type $arr
     * @return array<Category>
     */
    public function getAllCategoryNodesHaveRootSpecifiedID($id, $extension, $arr = NULL) {
	if (!$arr)
	    $arr = array();

	$category = $this->searchByParentID($id, $extension);
	foreach ($category as $data) {
	    $arr[] = $data;
	    $arr = $this->getAllCategoryNodesHaveRootSpecifiedID($data['id'], $extension, $arr);
	}
	return $arr;
    }

    public function CanDeleteCategory() {

	$criteria = new CDbCriteria();
	$criteria->compare('catid', $this->id);

	return Article::model()->count($criteria) > 0 ? false : true;
    }

    /**
     * Check the category is allowed to delete.
     * If the category has any child (article). don't allow to delete it
     * @param type $catId
     * @return boolean
     */
    public static function CanDeleteCategoryWithID($catId = NULL) {

	$criteria = new CDbCriteria();

	$criteria->compare('catid', $catId);
	$articles = Article::model()->findAll($criteria);

	return count($articles) > 0 ? false : true;
    }

    /**
     * Check if category exist on article or not
     * @param array $listID list ID to check
     * 
     * @return boolean true if categories is existed in Article else false
     */
    public function isExistsOnArticle($listID) {
	if (!isset($listID))
	    return false;
	else {
	    $criteria = new CDbCriteria();
	    $criteria->addInCondition('catid', $listID);
	    $list = Article::model()->findAll($criteria);
	    return $list ? true : false;
	}
    }

    public function beforeDelete() {
	if (parent::beforeDelete()) {
	    $list_child_category = $this->getAllCategoryNodesHaveRootSpecifiedID($this->id, $this->extension);

	    foreach ($list_child_category as $data) {
		$category = new Category();
		$category->deleteByPk($data['id']);
	    }
	    return true;
	} else {
	    return false;
	}
    }

    public function afterDelete() {
	$list_category = $this->getCategoryForUpdate($this->parent_id, $this->display_order, $this->extension);

	if (isset($list_category)) {
	    $category = new Category();
	}

	foreach ($list_category as $value) {
	    $category_entity = $category->model()->findByPk($value['id']);
	    $category_entity->display_order = (int) $value['display_order'] - 1;
	    $category_entity->saveAttributes(array('display_order'));
	}
	parent::afterDelete();
    }

    protected function afterFind() {
	$updateDateTimeSpan = strtotime($this->update_date);
	$this->update_date = date(LGConstant::DATE_FORMAT, $updateDateTimeSpan);

	if ($this->update_date == LGConstant::DATE_DEFAULT) {
	    $this->update_date = '';
	}

	$this->originalStatus = $this->status;

	parent::afterFind();
    }
    
        /**
     * Get all node have root node which has id is $id
     * @param type $id
     * @param type $extension
     * @param type $arr
     * @return array<Menu>
     */
    public function getAllNodesHaveRootSpecifiedID($id, $arr = NULL) {
	if (!$arr)
	    $arr = array();

	$category = $this->searchByParentID($id);
	foreach ($category as $data) {
	    $arr[] = $data;
	    $arr = $this->getAllNodesHaveRootSpecifiedID($data['id'], $arr);
	}
	return $arr;
    }
    
//   public function searchByParentID($parent_id) {
//	$criteria = new CDbCriteria();
//
//	$criteria->addCondition("parent_id = :parent_id");
//	$criteria->params = array(':parent_id' => $parent_id);
//	//$criteria->order = 'display_order ASC';
//	return $this->findAll($criteria);
//    }
    
    /*
     * When a category changes status, all its subs will be change to its status.
     * return true if updated successfully. Else return false
     */

    public function changeAllSubsStatus() {

	$menu = self::model()->getAllNodesHaveRootSpecifiedID($this->id);
	$isSuccess = true;

	//Create a sql command to update new status
	$sqlQuery = "UPDATE " . $this->tableName() . " SET status = $this->status WHERE 1 = 0 ";

	$temp = " OR id = ";
	foreach ($menu as $item) {
	    $id = $item->id;
	    $condition = $temp . $item->id;
	    $sqlQuery.= $condition;
	}

	$connection = Yii::app()->db;
	$command = $connection->createCommand($sqlQuery);
	try {
	    $rowCount = $command->execute(); // execute the non-query SQL
	} catch (Exception $exc) {
	    $isSuccess = false;
	}

	return $isSuccess;
    }

    /**
     * Data Tree For GridView
     * @param type $pageSize
     * @return \CActiveDataProvider
     */
    public static function getProductTypeTreeDataProvider($model = NULL, $pageSize = 0) {

	$criteria = new CDbCriteria();
	if ($model == NULL) {
	    $model = self::model();

	    $criteria->compare('status', 0);
//$criteria->order = 'parent_id ASC';
	} else {


	    $criteria->compare('id', $model->id);
	    $criteria->compare('parent_id', $model->parent_id);
	    $criteria->addSearchCondition('name', trim($model->name));
	    $criteria->compare('alias', $model->alias, true);
	    $criteria->compare('level', $model->level);
	    $criteria->compare('display_order', $model->display_order);
	    $criteria->compare('description', $model->description, true);
	    $criteria->compare('status', $model->status);
	    $criteria->compare('extension', $model->extension, true);
	    $criteria->compare('language', $model->language, true);
	    $criteria->compare('meta_description', $model->meta_description, true);
	    $criteria->compare('meta_keyword', $model->meta_keyword, true);
	    $criteria->compare('robots', $model->robots, true);
	    $criteria->compare('created_by', $model->created_by, true);
	    $criteria->compare('create_date', $model->create_date, true);
	    $criteria->compare('updated_by', $model->updated_by, true);
	    $criteria->compare('update_date', $model->update_date, true);
	    $criteria->compare('hits', $model->hits);
	}
	$criteria->order = 'parent_id ASC, display_order ASC';
	$pageSize = $pageSize == 0 ? $model->count($criteria) : $pageSize;


	$productTypes = new CActiveDataProvider($model, array(
	    'criteria' => $criteria,
	    'pagination' => array(
		'pageSize' => $model->count($criteria)
	    )
	));


	$level1Nodes = self::organizeTree($productTypes->data);
	$resultArr = self::convertTreeToArray($level1Nodes);

	if ($resultArr == NULL) {
	    $resultArr = array();
	}

	$pagination = new CPagination;
	$pagination->itemCount = $model->count($criteria);
	$pagination->pageSize = $pageSize;

	$page = isset($_GET['page']) ? $_GET['page'] : 1;
	if ($page > $pagination->itemCount / $pageSize + 1) {
	    $page = intval($pagination->itemCount / $pageSize) + 1;
	} else if ($page == $pagination->itemCount / $pageSize + 1) {
	     $page = intval($pagination->itemCount / $pageSize);
	}
	
	$resultArr = array_slice($resultArr, ($page - 1) * $pageSize, $pageSize);
	$productTypes->setData($resultArr);
	$productTypes->setPagination($pagination);

	return $productTypes;
    }

    /*
     * @param Menu $model
     */

    public static function getLevel($model) {
	if ($model == null) {
	    throw new InvalidArgumentException();
	}

	return self::getParentNodeLevel($model->parent_id);
    }

    /**
     * Using recursive to get parent node level
     * @param type $parentID
     * @return int
     */
    public static function getParentNodeLevel($parentID) {
	$parent = self::getParentProductType($parentID);

	if ($parent == NULL) {
	    return 0;
	}

	return self::getParentNodeLevel($parent->parent_id) + 1;
    }

    protected static function getParentProductType($parentID) {
	if ($parentID == NULL) {
	    return NULL;
	}

	return self::model()->findByPk($parentID);
    }

    /**
     * Data Tree For DropDownList. Must be remove it and it's child, if not creae
     * cycle relationship
     * @return type
     */
    public static function getProductTypeTreeArray($productTypeID = NULL) {
	$model = self::model();
	$criteria = new CDbCriteria();
	$criteria->compare('status', 0);
//$criteria->order = 'parent_id ASC';
	$criteria->order = 'parent_id ASC, display_order ASC';
	$pageSize = $model->count();

	$productTypes = new CActiveDataProvider($model, array(
	    'criteria' => $criteria,
	    'pagination' => false,
	));

	$level1Nodes = self::organizeTree($productTypes->data);

	if ($productTypeID != NULL) {
	    self::removeANode($level1Nodes, $productTypeID);
	}

	$resultArr = self::convertTreeToArray($level1Nodes);


	return $resultArr;
    }

    /**
     * 
     * @param type $nodes
     * @param type $nodeID
     * @return boolean false when have not found yet. esle return true
     */
    private static function removeANode(&$nodes, $nodeID /* productTypeID */) {

	if ($nodes == NULL)
	    return false;
	$lenght = count($nodes);
	for ($i = 0; $i < $lenght; $i++) {
	    $node = $nodes[$i];
	    if ($node != NULL) {
		if ($node->id == $nodeID) {
//             $nodes = array_diff($nodes, array($node));
		    array_splice($nodes, $i, 1);
//             if (isset($node->childs)) {
//                $node->childs = array();
//             }

		    return true;
		}


		if ($node->childs != NULL && count($node->childs) > 0) {
		    $isFound = self::removeANode($node->childs, $nodeID);

		    if ($isFound) {
			return true;
		    }
		}
	    }
	}

	return false;
    }

    /**      2
     *      /
     *     1
     *      \
     *       3---3.1
     * 
     *        5---5.1
     *       /
     *      4
     *       \
     *        6
     * @param type $productTypes
     * @return array
     */
    public static function organizeTree($productTypes) {
	$arr = array();
	$rootID = NULL;
	$level = -1;
	$productType = null;

//Contains root nodes
	$resultArr = array();

//Find items have parent_id not existed in array
//They are root nodes
	foreach ($productTypes as $m) {
	    $isExsit = false;
	    if ($m->parent_id == NULL) {
		array_push($arr, $m);
		array_push($resultArr, $m);
		continue;
	    }

	    foreach ($productTypes as $n) {
		if ($n->id == $m->parent_id) {
		    $isExsit = true;
		    break;
		}
	    }

	    if (!$isExsit) {
		array_push($arr, $m);
		array_push($resultArr, $m);
	    }
	}

//Find child of item in $arr, and add childs for them
	do {
	    $productType = array_pop($arr);

	    if ($productType != NULL) {
		$rootID = $productType->id;
		$level = $productType->level;
	    }

	    foreach ($productTypes as $value) {
		if ($value->parent_id == $rootID) {
		    $value->level = $level + 1;
		    $arr[] = $value;

		    array_push($productType->childs, $value);
		}
	    }
	} while ($productType != NULL);

	return $resultArr;
    }

    public static function convertTreeToArray($level1Nodes) {
//      $temp = new SOProductType;
//      $temp->childs = $level1Nodes;
//      $temp->sortChilds();
	if (count($level1Nodes) > 0) {

	    $clonedArr = (array) clone(object) $level1Nodes;
	    $sortLevel1Nodes = array();
	    while (count($clonedArr) > 0) {
		$arrKey = array_keys($clonedArr);
		$selectingNode = $clonedArr[$arrKey[0]];
		
		array_splice($clonedArr, 0, 1);
		$group = array($selectingNode);

		foreach ($clonedArr as $node) {
		    if ($node->parent_id == $selectingNode->parent_id) {
			array_push($group, $node);
		    }
		}

		$temp = self::model();
		$temp->childs = $group;
		$temp->sortChilds();

		$sortLevel1Nodes = array_merge($sortLevel1Nodes, $temp->childs);
		foreach ($group as $node) {
		    if (($key = array_search($node, $clonedArr)) !== false) {
			unset($clonedArr[$key]);
		    }
		}
		//$clonedArr = array_udiff($clonedArr, $group, 'compare_objects');
	    }

//	    $level1Nodes[0]->isUp = NULL;
//	    end($level1Nodes)->isDown = NULL;

	    $arr = $sortLevel1Nodes;

	    for ($i = 0; $i < count($arr); $i++) {
		$node = $arr[$i];

		if ($node == NULL || $node->childs == NULL || count($node->childs) == 0) {
		    continue;
		    ;
		}

		$node->sortChilds();

		array_splice($arr, $i + 1, 0, $node->childs);
	    }

	    return $arr;
	}
    }

    public function sortChilds() {
	if ($this->childs != NULL && count($this->childs) > 0) {
	    usort($this->childs, "cmp");
	    $length = count($this->childs);

	    for ($i = 0; $i < $length; $i++) {
		if ($i - 1 < 0) {
		    $this->childs[$i]->isUp = NULL;
		} else {
		    $this->childs[$i]->isUp = $this->childs[$i - 1]->id;
		}

		if ($i + 1 >= $length) {
		    $this->childs[$i]->isDown = NULL;
		} else {
		    $this->childs[$i]->isDown = $this->childs[$i + 1]->id;
		}
	    }
	}
    }

}